#!/bin/bash -x

work_top_dir="$HOME/workspace/"

anolisos_certs_path="/usr/share/pki/ca-trust-source/anchors/"
anolisos_ca_bundle="/etc/pki/tls/certs/ca-bundle.crt"

registry_image_tag="docker.io/library/registry:2"
registry_name="my-local-registry"
registry_openssl_cnf="${work_top_dir}/${registry_name}.openssl.cnf"
registry_certs_dir="${work_top_dir}/${registry_name}.certs"
registry_ip_addr=""
registry_port=31313
registry_k8s_tls_secret_name="${registry_name}-tls"
registry_k8s_service_name="${registry_name}-service"
registry_k8s_pod_yaml="${work_top_dir}/${registry_name}.yaml"

usage () {
  set +x
  echo -e "\033[31m
Usage: $0 <-h|-i <ip-addr>> [-p <port>]

       -h,--help Display this help message
       -i,--ip   Specify the ip address of the local registry
       -p,--port Specify the port number of the local registry
                 Valid range is [30000,32767], default is 31313
\033[0m"
  set -x
}

if [ $# -eq 0 ]; then
  usage
  exit 1
fi

params=$(getopt -o hi:p: --long help,ip:,port: -- "$@")
if [ $? -ne 0 ]; then
  echo "[Error] cannot parse parameters"
  exit 1
fi

eval set -- "$params"

while true; do
  case "$1" in
  -h|--help)
    usage
    exit 0
    ;;
  -i|--ip)
    registry_ip_addr="$2"
    shift 2
    ;;
  -p|--port)
    registry_port="$2"
    shift 2
    ;;
  --)
    shift
    break
    ;;
  *)
    usage
    exit 1
    ;;
  esac
done

if [ ! -d ${work_top_dir} ]; then
  mkdir -p ${work_top_dir}
fi

generate_keypair_certs_for_local_registry () {
  echo "[Generate key-pair and certs for local registry]"
  # Remove obsolete certs of the local registry
  echo "[Remove obsolete certs of the local registry]"
  sudo yum update -y ca-certificates
  sudo rm ${anolisos_certs_path}/domain.crt || true
  sudo update-ca-trust extract

  rm -rf ${registry_certs_dir} || true
  mkdir -p ${registry_certs_dir}

  # Generate openssl config for local registry
  cat <<EOF | tee ${registry_openssl_cnf}
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
req_extensions = req_ext

[dn]
C = US
ST = State
L = City
O = Organization
OU = Unit
CN = ${registry_ip_addr}

[req_ext]
subjectAltName = @alt_names

[alt_names]
IP.1 = ${registry_ip_addr}
EOF

  # Generate certs, key
  echo "[Generate certs and keys for the local registry]"
  openssl genrsa -out ${registry_certs_dir}/domain.key 2048
  openssl req -new -key ${registry_certs_dir}/domain.key \
    -out ${registry_certs_dir}/domain.csr \
    -config ${registry_openssl_cnf}
  openssl x509 -req -in ${registry_certs_dir}/domain.csr \
    -signkey ${registry_certs_dir}/domain.key \
    -out ${registry_certs_dir}/domain.crt \
    -days 365 -extensions req_ext \
    -extfile ${registry_openssl_cnf}

  # Update cert of the local registry to AnolisOS ca-bundle
  echo "[Update cert of the local registry to AnolisOS ca-bundle]"
  sudo cp -i ${registry_certs_dir}/domain.crt ${anolisos_certs_path}/
  sudo update-ca-trust extract
  cat ${registry_certs_dir}/domain.crt | sudo tee -a ${anolisos_ca_bundle}

  # Update certs for docker & containerd
  echo "[Update cert of the local registry to docker & containerd services]"
  containerd_registry_cert_dir="/etc/containerd/certs.d/${registry_ip_addr}:${registry_port}"
  docker_registry_cert_dir="/etc/docker/certs.d/${registry_ip_addr}:${registry_port}"
  sudo mkdir -p ${containerd_registry_cert_dir}
  sudo cp -a ${registry_certs_dir}/domain.crt \
    ${containerd_registry_cert_dir}/ca.crt
  sudo mkdir -p ${docker_registry_cert_dir}
  sudo cp -a ${registry_certs_dir}/domain.crt \
    ${docker_registry_cert_dir}/ca.crt
  sudo systemctl daemon-reload
  sudo systemctl restart containerd
  sudo systemctl restart docker
  sleep 10
}

generate_keypair_certs_for_local_registry

generate_local_registry_pod_yaml () {
  echo "[Generate Pod yaml for local registry]"

  # Pull the registry image in advance
  sudo -E ctr -n k8s.io image pull ${registry_image_tag}

  cat <<EOF | tee ${registry_k8s_pod_yaml}
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ${registry_name}
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ${registry_name}
  template:
    metadata:
      labels:
        app: ${registry_name}
    spec:
      containers:
      - name: ${registry_name}
        image: ${registry_image_tag}
        ports:
        - containerPort: 5000
        env:
        - name: REGISTRY_HTTP_TLS_CERTIFICATE
          value: /$(basename ${registry_certs_dir})/domain.crt
        - name: REGISTRY_HTTP_TLS_KEY
          value: /$(basename ${registry_certs_dir})/domain.key
        volumeMounts:
        - name: mount-certs
          mountPath: /$(basename ${registry_certs_dir})
      volumes:
      - name: mount-certs
        secret:
          secretName: ${registry_k8s_tls_secret_name}
---
apiVersion: v1
kind: Service
metadata:
  name: ${registry_k8s_service_name}
  labels:
    app: ${registry_name}
spec:
  selector:
    app: ${registry_name}
  ports:
    - protocol: TCP
      port: 5000
      targetPort: 5000
      nodePort: ${registry_port}
  type: NodePort
EOF

  # Delete local registry before update k8s registry secret
  kubectl delete -f ${registry_k8s_pod_yaml} || true
}

generate_local_registry_pod_yaml

register_registry_key_certs () {
  # Create k8s secret
  echo "[Create k8s secret for the local registry]"
  kubectl delete secret ${registry_k8s_tls_secret_name} || true
  kubectl create secret generic ${registry_k8s_tls_secret_name} \
    --from-file=domain.crt=${registry_certs_dir}/domain.crt \
    --from-file=domain.key=${registry_certs_dir}/domain.key
}

register_registry_key_certs

# Deploy registry
echo "[Deploy the local registry]"
kubectl apply -f ${registry_k8s_pod_yaml}

# Check registry service
echo "[Get k8s service for the local registry]"
kubectl get svc -l app=${registry_name}
sleep 5

# Push test image to local registry
push_test_image_to_local_registry () {
  local test_image_tag="busybox:latest"
  local local_registry="${registry_ip_addr}:${registry_port}"
  local local_registry_test_image_tag="${local_registry}/${test_image_tag}"

  echo "[Push test image to local registry]"
  docker image pull ${test_image_tag}
  docker image tag ${test_image_tag} ${local_registry_test_image_tag}
  docker image push ${local_registry_test_image_tag}
}

push_test_image_to_local_registry
